#include "stdafx.h"
#include "CScrollBase.h"

CScrollBase::CScrollBase()
	: m_fLine(1.0f), m_fPage(10.0f), m_eDirection(AlignVertical), m_eScrollPoint(ScrollPointInvalid),
	m_bDraging(false), m_fDragOff(0.0f), m_pHost(nullptr)
{

}

CScrollBase::CScrollBase(int_32 iWidth, int_32 iHeight)
	: m_siScroll(iWidth, iHeight), m_fLine(1.0f), m_fPage(10.0f), m_eDirection(AlignVertical), m_eScrollPoint(ScrollPointInvalid),
	m_bDraging(false), m_fDragOff(0.0f), m_pHost(nullptr)
{

}

void CScrollBase::SetScrollHost(IScrollHost * pHost)
{
	m_pHost = pHost;
}
IScrollHost * CScrollBase::GetScrollHost() const
{
	return m_pHost;
}
void CScrollBase::SetScrollSize(const CSizeInt32 & siScroll)
{
	m_siScroll = siScroll;
}

CSizeInt32 CScrollBase::GetScrollSize() const
{
	return m_siScroll;
}

void CScrollBase::SetDirection(AlignType eDirection)
{
	switch(eDirection)
	{
	case AlignHorizon:
	case AlignVertical:
		break;
	default:
		throw CIllegalArgumentException();
	}

	m_eDirection = eDirection;
}

void CScrollBase::SetLineValue(float_32 fLine)
{
	VerifyArgument(fLine < m_fMax - m_fMin);

	if(!Float32Equal(fLine, m_fLine))
	{
		m_fLine = fLine;
		LineValueChanged();
	}
}

void CScrollBase::SetPageValue(float_32 fPage)
{
	VerifyArgument(fPage < m_fMax - m_fMin);

	if(!Float32Equal(fPage, m_fPage))
	{
		m_fPage = fPage;
		PageValueChanged();
	}
}

void CScrollBase::SetScrollInfo(float_32 fMin, float_32 fMax, float_32 fValue, float_32 fLine, float_32 fPage)
{
	float_32 fTotal = m_fMax - m_fMin;
	if(fMin > fMax || fValue < fMin || fValue > fMax || 
		fLine < 0 || fLine > fTotal || fPage > fTotal)
		throw CIllegalArgumentException();

	SetRangeInfo(fMin, fMax, fValue);

	bool bLile = !Float32Equal(fLine, m_fLine);
	bool bPage = !Float32Equal(fPage, m_fPage);
	if(bLile || bPage)
	{
		m_fLine = fLine;
		m_fPage = fPage;
		if(bLile)
			LineValueChanged();
		if(bPage)
			PageValueChanged();
	}
}
void CScrollBase::SetScrollInfo(const ScrollInfo & scroll)
{
	SetScrollInfo(scroll.MinValue, scroll.MaxValue, scroll.Value, scroll.LineValue, scroll.PageValue);
}

ScrollInfo CScrollBase::GetScrollInfo() const
{
	return ScrollInfo(m_fMin, m_fMax, m_fValue, m_fLine, m_fPage);
}

void CScrollBase::LineUp()
{
	SetValue(m_fValue - m_fLine);
}

void CScrollBase::PageUp()
{
	SetValue(m_fValue - m_fPage);
}

void CScrollBase::LineDown()
{
	SetValue(m_fValue + m_fLine);
}

void CScrollBase::PageDown()
{
	SetValue(m_fValue + m_fPage);
}

ScrollPoint CScrollBase::GetMouseScrollPoint(const CPointFloat32 & ptMouse, float_32 * pfBarOff/* = nullptr*/) const
{
	if(m_eDirection == AlignVertical)
	{
		if(ptMouse.X < 0.0f || ptMouse.X > m_siScroll.Width || ptMouse.Y < 0)
			return ScrollPointInvalid;

		float_32 fSliderH = m_siScroll.Height - m_siScroll.Width * 2.0f;
		float_32 fTotal = m_fMax - m_fMin + m_fPage;

		float_32 fBarY = fSliderH * m_fValue / fTotal;
		float_32 fBarH = fSliderH * m_fPage / fTotal;

		if(ptMouse.Y < m_siScroll.Width)
			return ScrollPointLineUp;
		else if(ptMouse.Y < m_siScroll.Width + fBarY)
			return ScrollPointPageUp;
		else if(ptMouse.Y < m_siScroll.Width + fBarY + fBarH)
		{
			if(pfBarOff)
				*pfBarOff = ptMouse.Y - (m_siScroll.Width + fBarY);
			return ScrollPointBar;
		}
		else if(ptMouse.Y < m_siScroll.Height - m_siScroll.Width)
			return ScrollPointPageDown;
		else if(ptMouse.Y < m_siScroll.Height)
			return ScrollPointLineDown;
		else
			return ScrollPointInvalid;
	}
	else if(m_eDirection == AlignHorizon)
	{
		if(ptMouse.Y < 0.0f || ptMouse.Y > m_siScroll.Height|| ptMouse.X < 0)
			return ScrollPointInvalid;

		float_32 fSliderW = m_siScroll.Width - m_siScroll.Height * 2.0f;
		float_32 fTotal = m_fMax - m_fMin + m_fPage;

		float_32 fBarX = fSliderW * m_fValue / fTotal;
		float_32 fBarW = fSliderW * m_fPage / fTotal;

		if(ptMouse.X < m_siScroll.Height)
			return ScrollPointLineUp;
		else if(ptMouse.X < m_siScroll.Height + fBarX)
			return ScrollPointPageUp;
		else if(ptMouse.X < m_siScroll.Height + fBarX + fBarW)
		{
			if(pfBarOff)
				*pfBarOff = ptMouse.X - (m_siScroll.Height + fBarX);
			return ScrollPointBar;
		}
		else if(ptMouse.X < m_siScroll.Width - m_siScroll.Height)
			return ScrollPointPageDown;
		else if(ptMouse.X < m_siScroll.Width)
			return ScrollPointLineDown;
		else
			return ScrollPointInvalid;
	}
	else
		throw CBadStateException();
}

void CScrollBase::SetScrollPoint(ScrollPoint eScrollPoint)
{
	if(eScrollPoint != m_eScrollPoint)
	{
		m_eScrollPoint = eScrollPoint;
		ScrollPointChanged();
	}
}

void CScrollBase::MinValueChanged()
{
	if(m_pHost)
	{
		m_pHost->MinValueChanged(this);
		m_pHost->RepaintScroll();
	}
}

void CScrollBase::MaxValueChanged()
{
	if(m_pHost)
	{
		m_pHost->MaxValueChanged(this);
		m_pHost->RepaintScroll();
	}
}
void CScrollBase::ValueChanged()
{
	if(m_pHost)
	{
		m_pHost->ValueChanged(this);
		m_pHost->RepaintScroll();
	}
}
void CScrollBase::ScrollSizeChanged()
{
	if(m_pHost)
	{
		m_pHost->ScrollSizeChanged(this);
		m_pHost->RepaintScroll();
	}
}
void CScrollBase::LineValueChanged()
{
	if(m_pHost)
	{
		m_pHost->LineValueChanged(this);
		m_pHost->RepaintScroll();
	}
}

void CScrollBase::PageValueChanged()
{
	if(m_pHost)
	{
		m_pHost->PageValueChanged(this);
		m_pHost->RepaintScroll();
	}
}

void CScrollBase::ScrollPointChanged()
{
	if(m_pHost)
	{
		m_pHost->ScrollPointChanged(this);
		m_pHost->RepaintScroll();
	}
}

void CScrollBase::ScrollMouseIn(CMouseArgument & args)
{
}

void CScrollBase::ScrollMouseMove(CMouseArgument & args)
{
	if(m_bDraging)
	{
		if(m_eDirection == AlignVertical)
		{
			float_32 fBarY = args.m_ptMouse.Y - m_fDragOff;
			float_32 fRate = (fBarY -  m_siScroll.Width) / (m_siScroll.Height - m_siScroll.Width * 2.0f);

			float_32 fValue = (m_fMax - m_fMin + m_fPage) * fRate;
			SetValue(fValue);
		}
		else if(m_eDirection == AlignHorizon)
		{
			float_32 fBarX = args.m_ptMouse.X - m_fDragOff;
			float_32 fRate = (fBarX -  m_siScroll.Height) / (m_siScroll.Width - m_siScroll.Height * 2.0f);

			float_32 fValue = (m_fMax - m_fMin + m_fPage) * fRate;
			SetValue(fValue);
		}
		else
			throw CBadStateException();
	}
	else
		SetScrollPoint(GetMouseScrollPoint(args.m_ptMouse));
}
void CScrollBase::ScrollMouseDown(CMouseArgument & args)
{
	if(args.m_eButton == MouseButtonLeft)
	{
		ScrollPoint sp = GetMouseScrollPoint(args.m_ptMouse, &m_fDragOff);
		switch(sp)
		{
		case ScrollPointBar:
			m_bDraging = true;
			break;
		case ScrollPointLineUp:
			LineUp();
			break;
		case ScrollPointPageUp:
			PageUp();
			break;
		case ScrollPointPageDown:
			PageDown();
			break;
		case ScrollPointLineDown:
			LineDown();
			break;
		default:
			break;
		}
	}
}

void CScrollBase::ScrollMouseUp(CMouseArgument & args)
{
	if(m_bDraging)
		m_bDraging = false;
	SetScrollPoint(GetMouseScrollPoint(args.m_ptMouse));
}

void CScrollBase::ScrollMouseOut(CMouseArgument & args)
{
	SetScrollPoint(ScrollPointInvalid);
}

bool CScrollBase::IsDraging() const
{
	return m_bDraging;
}

void CScrollBase::OnPaintScroll(IPaint * pPaint, const CImageState & imgState, const CBaseAttr & baseAttr, const CMouseState & mouseState) const
{
	typedef void (CScrollBase::*DrawFuncT)(IPaint * pPaint, const CImageState & imgState, const CBaseAttr & baseAttr, const CMouseState & mouseState) const;
	DrawFuncT pfnDraw = nullptr;
	switch(m_eDirection)
	{
	case AlignVertical:
		switch(imgState.eImageTileMode)
		{
		case ImageTileModeH4:
			pfnDraw = &CScrollBase::DrawScrollH4V;
			break;
		}
		break;
	case AlignHorizon:
		switch(imgState.eImageTileMode)
		{
		case ImageTileModeH4:
			pfnDraw = &CScrollBase::DrawScrollH4H;
			break;
		}
		break;
	default:
		break;
	}
	if(!pfnDraw)
		throw CBadStateException();

	(this->*pfnDraw)(pPaint, imgState, baseAttr, mouseState);
}
void CScrollBase::DrawScrollH4V(IPaint * pPaint, const CImageState & imgState, const CBaseAttr & baseAttr, const CMouseState & mouseState) const
{
	IImage * pImage = imgState.pImage;
	CSizeFloat32 siImage = pImage->GetSize();
	CSizeFloat32 siScroll = m_siScroll;

	float_32 fSliderH = siScroll.Height - siScroll.Width * 2.0f;
	float_32 fTotal = m_fMax - m_fMin + m_fPage;

	float_32 fBarY = fSliderH * m_fValue / fTotal;
	float_32 fBarH = fSliderH * m_fPage / fTotal;

	CRectFloat32 rcLineUp(0.0f, 0.0f, siScroll.Width, siScroll.Width);
	CRectFloat32 rcPageUp(0.0f, siScroll.Width, siScroll.Width, fBarY);
	CRectFloat32 rcBar(0.0f, siScroll.Width + fBarY, siScroll.Width, fBarH);
	CRectFloat32 rcPageDown(0.0f, siScroll.Width + fBarY + fBarH, siScroll.Width, fSliderH - fBarY - fBarH);
	CRectFloat32 rcLineDown(0.0f, siScroll.Height - siScroll.Width, siScroll.Width, siScroll.Width);

	float_32 fSrcW = siImage.Width * 0.25f;
	CRectFloat32 srcLineUp(0.0f, 0.0f, fSrcW, fSrcW);
	CRectFloat32 srcPageUp(0.0f, fSrcW, fSrcW, fSrcW);
	CRectFloat32 srcBar(0.0f, fSrcW * 2.0f, fSrcW, fSrcW);
	CRectFloat32 srcPageDown(0.0f, fSrcW, fSrcW, fSrcW);
	CRectFloat32 srcLineDown(0.0f, fSrcW * 3.0f, fSrcW, fSrcW);

	if(baseAttr.Disabled)
	{
		srcLineUp.X = srcPageUp.X = srcBar.X = srcLineDown.X = fSrcW * 3.0f;
	}
	else
	{
		float_32 fX = 0.0f;
		if(mouseState.MouseDownL)
			fX = fSrcW * 2.0f;
		else if(mouseState.MouseHoving)
			fX = fSrcW;
		else
			fX = 0.0f;

		switch(m_eScrollPoint)
		{
		case ScrollPointLineUp:
			srcLineUp.X = fX;
			break;
		case ScrollPointPageUp:
			srcPageUp.X = fX;
			break;
		case ScrollPointBar:
			srcBar.X = fX;
			break;
		case ScrollPointPageDown:
			srcPageDown.X = fX;
			break;
		case ScrollPointLineDown:
			srcLineDown.X = fX;
			break;
		case ScrollPointInvalid:
			break;
		default:
			throw CBadStateException();
		}
	}

	pPaint->DrawImage(pImage, rcLineUp, srcLineUp);
	pPaint->DrawImageGridV3V(pImage, rcPageUp, srcPageUp);
	pPaint->DrawImageGridV3V(pImage, rcBar, srcBar);
	pPaint->DrawImageGridV3V(pImage, rcPageDown, srcPageDown);
	pPaint->DrawImage(pImage, rcLineDown, srcLineDown);
}
void CScrollBase::DrawScrollH4H(IPaint * pPaint, const CImageState & imgState, const CBaseAttr & baseAttr, const CMouseState & mouseState) const
{
	IImage * pImage = imgState.pImage;
	CSizeFloat32 siImage = pImage->GetSize();
	CSizeFloat32 siScroll = m_siScroll;

	float_32 fSliderW = siScroll.Width - siScroll.Height * 2.0f;
	float_32 fTotal = m_fMax - m_fMin + m_fPage;

	float_32 fBarX = fSliderW * m_fValue / fTotal;
	float_32 fBarW = fSliderW * m_fPage / fTotal;

	CRectFloat32 rcLineLeft(0.0f, 0.0f, siScroll.Height, siScroll.Height);
	CRectFloat32 rcPageLeft(siScroll.Height, 0.0f, fBarX, siScroll.Height);
	CRectFloat32 rcBar(siScroll.Height + fBarX, 0, fBarW, siScroll.Height);
	CRectFloat32 rcPageRight(siScroll.Height + fBarX + fBarW, 0.0f, fSliderW - fBarX - fBarW, siScroll.Height);
	CRectFloat32 rcLineRight(siScroll.Width - siScroll.Height, 0.0f, siScroll.Height, siScroll.Height);

	float_32 fSrcW = siImage.Height * 0.25f;
	CRectFloat32 srcLineLeft(0.0f, 0.0f, fSrcW, fSrcW);
	CRectFloat32 srcPageLeft(0.0f, fSrcW, fSrcW, fSrcW);
	CRectFloat32 srcBar(0.0f, fSrcW * 2.0f, fSrcW, fSrcW);
	CRectFloat32 srcPageRight(0.0f, fSrcW, fSrcW, fSrcW);
	CRectFloat32 srcLineRight(0.0f, fSrcW * 3.0f, fSrcW, fSrcW);

	if(baseAttr.Disabled)
	{
		srcLineLeft.X = srcPageLeft.X = srcBar.X = srcLineRight.X = fSrcW * 3.0f;
	}
	else
	{
		float_32 fX = 0.0f;
		if(mouseState.MouseDownL)
			fX = fSrcW * 2.0f;
		else if(mouseState.MouseHoving)
			fX = fSrcW;
		else
			fX = 0.0f;

		switch(m_eScrollPoint)
		{
		case ScrollPointLineUp:
			srcLineLeft.X = fX;
			break;
		case ScrollPointPageUp:
			srcPageLeft.X = fX;
			break;
		case ScrollPointBar:
			srcBar.X = fX;
			break;
		case ScrollPointPageDown:
			srcPageRight.X = fX;
			break;
		case ScrollPointLineDown:
			srcLineRight.X = fX;
			break;
		case ScrollPointInvalid:
			break;
		default:
			throw CBadStateException();
		}
	}

	pPaint->DrawImage(pImage, rcLineLeft, srcLineLeft);
	pPaint->DrawImageGridV3V(pImage, rcPageLeft, srcPageLeft);
	pPaint->DrawImageGridV3V(pImage, rcBar, srcBar);
	pPaint->DrawImageGridV3V(pImage, rcPageRight, srcPageRight);
	pPaint->DrawImage(pImage, rcLineRight, srcLineRight);
}
